#include <chrono>
#include <sys/stat.h>
#include <sys/types.h>
#include <cstring>
#include <string>
#include <filesystem>
#include <utils.h>
#include <algorithm>
#include <sstream>
#include <thread>

namespace utils
{
    std::string& replace(std::string& str, const std::string& oldSub, const std::string& newSub)
    {
        if (oldSub.empty())
        {
            return str;
        }

        std::size_t pos = 0;
        const std::size_t oldLen = oldSub.length();
        const std::size_t newLen = newSub.length();

        while ((pos = str.find(oldSub, pos)) != std::string::npos)
        {
            str.replace(pos, oldLen, newSub);
            pos += newLen;
        }

        return str;
    }
    std::string& trim(std::string& s, const std::string& trim_chars)
    {
        ltrim(s, trim_chars);
        rtrim(s, trim_chars);
        return s;
    }
    std::string& ltrim(std::string& s, const std::string& trim_chars)
    {
        if (s.empty()) 
        {
            return s;
        }

        size_t first_pos = s.find_first_not_of(trim_chars);

        if (first_pos == std::string::npos) 
        {
            s.clear();
        }
        else 
        {
            s.erase(0, first_pos);
        }

        return s;
    }
    std::string& rtrim(std::string& s, const std::string& trim_chars)
    {
        if (s.empty()) 
        {
            return s;
        }

        size_t last_pos = s.find_last_not_of(trim_chars);

        if (last_pos == std::string::npos) 
        {
            s.clear();
        }
        else 
        {
            s.erase(last_pos + 1);
        }

        return s;
    }
    std::string& trim(std::string& s)
    {
        std::string space(SPACES);
        return trim(s, space);
    }
    std::string& ltrim(std::string& s)
    {
        std::string space(SPACES);
        return ltrim(s, space);
    }
    std::string& rtrim(std::string& s)
    {
        std::string space(SPACES);
        return rtrim(s, space);
    }
    std::vector<std::string> split(const std::string& str)
    {
        std::string space(SPACES);
        return split(str, space);
    }

    std::vector<std::string> split(const std::string& str, const std::string& delimiter) 
    {
        std::vector<std::string> result;
        auto trimPush = [&](const std::string &s)
        {
            std::string trimmed(s);
            
            trimmed = trim(trimmed);
            trimmed.erase(std::remove_if(trimmed.begin(), trimmed.end(), [](unsigned char c)
                                        { return c == CR_CHAR; }),
                        trimmed.end());

            if (!trimmed.empty()) 
            {
                result.push_back(trimmed);
            }
        };

        if (delimiter.empty()) 
        {
            trimPush(str);
            return result;
        }

        const size_t str_len = str.length();
        const size_t delimiter_len = delimiter.length();
        size_t current_pos = 0;
        bool is_in_quote = false;

        while (current_pos < str_len) 
        {
            if (str[current_pos] == DQUOTE && !is_in_quote) 
            {
                is_in_quote = true;
                current_pos++;

                size_t next_quote = str.find(DQUOTE, current_pos);
                if (next_quote == std::string::npos) 
                {
                    next_quote = str_len;
                }

                trimPush(str.substr(current_pos, next_quote - current_pos));
                current_pos = next_quote + 1;
                is_in_quote = false;
            } 
            else 
            {
                size_t next_delimiter = str.find(delimiter, current_pos);
                
                if (next_delimiter == std::string::npos) 
                {
                    next_delimiter = str_len;
                }

                trimPush(std::move(str.substr(current_pos, next_delimiter - current_pos)));
                current_pos = next_delimiter + delimiter_len;
            }
        }

        return result;
    }

    std::string byteToString(const char* s, size_t size)
    {
        if (!s && size > 0) 
        {
            return NULLSTR;
        }
        std::string buffer;
        buffer.reserve(size * 2);
        for (size_t i = 0; i < size; i++)
        {
            buffer.append(utils::format("%02X", s[i]));
        }
        return buffer;
    }

    std::string byteToString(const std::string& str)
    {
        return byteToString(str.data(), str.size());
    }
    std::string byteToString(const std::vector<BYTE>& data)
    {
        if (data.empty())
        {
            return NULLSTR;
        }
        return byteToString(reinterpret_cast<const char*>(data.data()), data.size());
    }
    bool stricmp(const std::string& s1, const std::string& s2) {

        if (s1.length() != s2.length()) return false;

        return std::equal(s1.begin(), s1.end(), s2.begin(),
            [](char a, char b) {
                return tolower(static_cast<unsigned char>(a)) ==
                       tolower(static_cast<unsigned char>(b));
            });
    }

    STATUS mkdirPath(const std::string& path)
    {
        if (path.empty())
        {
            return RET_ERROR;
        }

        try
        {
            std::filesystem::path dirPath(path);

#ifdef _WIN32
            if (dirPath.string().length() > 248)
            {
                std::string winPath = "\\\\?\\" + dirPath.make_preferred().string();
                dirPath = std::filesystem::path(winPath);
            }
            else
            {
                dirPath = dirPath.make_preferred();
            }
#endif
            std::filesystem::create_directories(dirPath);

            if (std::filesystem::exists(dirPath) && std::filesystem::is_directory(dirPath))
            {
#ifndef _WIN32
                auto dir_perms = std::filesystem::perms::owner_read |
                    std::filesystem::perms::owner_write |
                    std::filesystem::perms::owner_exec |
                    std::filesystem::perms::group_read |
                    std::filesystem::perms::group_exec |
                    std::filesystem::perms::others_read |
                    std::filesystem::perms::others_exec;

                auto current_perms = std::filesystem::status(dirPath).permissions();
                std::filesystem::permissions(dirPath, current_perms | dir_perms);
#endif
                return OK;
            }
            else
            {
                return RET_ERROR;
            }
        }
        catch (...)
        {
            return RET_ERROR;
        }
    }
EXTERN_C_BEGIN
    bool stricmp(const char* s1, const char* s2)
    {

        if (s1 == nullptr && s2 == nullptr) return true;
        if (s1 == nullptr || s2 == nullptr) return false;

        return stricmp(std::string(s1), std::string(s2));
    }

    uint64_t GetCurrentMillisecs()
    {
        auto now = std::chrono::system_clock::now();
        auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(
            now.time_since_epoch());

        return static_cast<uint64_t>(millis.count());
    }

    bool localtime_rs(struct tm* const local_time, time_t const* const now_t)
    {
        bool time_convert_ok = false;

#ifdef _WIN32
        time_convert_ok = (::localtime_s(local_time, now_t) == 0);
#else
        time_convert_ok = (::localtime_r(now_t, local_time) != nullptr);
#endif

        return time_convert_ok;
    }

    /**
     * @brief High-precision delay function that pauses execution for a specified number of seconds
     * 
     * Supports fractional seconds (e.g., 1.5 = 1 second 500 milliseconds) with system-max precision
     * (typically microsecond-level, nanosecond-level on some systems). Handles spurious wakeups
     * and balances precision with CPU efficiency.
     * 
     * @param seconds Delay duration in seconds (must be positive)
     */

    void taskDelay(double seconds) 
    {
        if (seconds <= 0.0) 
        {
            return;
        }

        auto start = std::chrono::high_resolution_clock::now();
        auto target = start + std::chrono::duration<double>(seconds);
        
        auto remaining = target - std::chrono::high_resolution_clock::now();
        if (remaining > std::chrono::milliseconds(1)) 
        {
            std::this_thread::sleep_until(target);
        }
        else 
        {
            while (true) 
            {
                auto now = std::chrono::high_resolution_clock::now();
                if (now >= target) 
                {
                    break;
                }

                auto remainingNs = std::chrono::duration_cast<std::chrono::nanoseconds>(target - now);
                auto remainingUs = std::chrono::duration_cast<std::chrono::microseconds>(remainingNs);
                
                if (remainingUs > std::chrono::microseconds(100)) 
                {
                    std::this_thread::sleep_for(remainingNs * 9 / 10);
                } 
                else 
                {
                    std::this_thread::yield();
                }
            }
        }
    }
EXTERN_C_END
} // namespace utils
